/**
 * @ Author: luoqi
 * @ Create Time: 2024-07-12 09:27
 * @ Modified by: luoqi
 * @ Modified time: 2025-11-07 00:33
 * @ Description:
 */
#include <stdarg.h>
#include <stdbool.h>
#include <printf.h>
#include <ringbuf.h>
#include <qmem.h>
#include <cli.h>
#include "bsp.h"

extern void SystemClock_Config(void);

static QTaskSched sched;

static RingBuffer rb_usb_rx;
static uint8_t rb_usb_rx_buf[1538] CCMRAM;

static bool is_usb_tx_ready = true;

static QMem mempool;
static uint8_t mempool_buf[1024] CCMRAM;

static int _rcc_reset()
{
    RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
    if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) {
        Error_Handler();
    }
    return 0;
}

static int _cmd_info(int argc, char *argv[])
{
    UNUSED(argc);
    UNUSED(argv);

    QSH("\033[H\033[2J");
    QSH("  ___  ___  ___   ___ _____\r\n");
    QSH(" / _ \\| _ )/ _ \\ / _ \\_   _|\r\n");
    QSH("| (_) | _ \\ (_) | (_) || |\r\n");
    QSH(" \\__\\_\\___/\\___/ \\___/ |_|\r\n");
    QSH(" QBOOT FIRMWARE, %s, %s\r\n", __DATE__, __TIME__);
    QSH(" Designed by luoqi\r\n");

    return 0;
}

static void _usb_reenumerate()
{
    GPIO_InitTypeDef GPIO_InitStruct;
    __HAL_RCC_GPIOB_CLK_ENABLE();
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET);
    GPIO_InitStruct.Pin = GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET);
    uint32_t start = ktime();
    while(ktime() - start < 100);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET);
    while(ktime() - start < 100);

}

// static RingBuffer rb_usb_tx;
// static uint8_t rb_usb_tx_buf[1024];

// static void _task_usb_send(void)
// {
//     uint8_t buf[256] = { 0 };
//     int sz = rb_read(&rb_usb_tx, buf, sizeof(buf));
//     if(sz > 0) {
//         bsp_send(buf, sz, 10);
//     }
// }

int bsp_init(void)
{
    HAL_Init();
    _rcc_reset();
    SystemClock_Config();

    qmem_init(&mempool, mempool_buf, sizeof(mempool_buf), 4, 8, 0x55, NULL, NULL);
    rb_init(&rb_usb_rx, rb_usb_rx_buf, sizeof(rb_usb_rx_buf), QNULL, QNULL);
    // rb_init(&rb_usb_tx, rb_usb_tx_buf, sizeof(rb_usb_tx_buf), QNULL, QNULL);

    MX_GPIO_Init();

    bsp_led_set(LED_BLINK, BSP_LED_OFF);
    bsp_led_set(LED_LINK, BSP_LED_OFF);
    bsp_led_set(LED_ERROR, BSP_LED_ON);

    MX_SPI1_Init();
    MX_DMA_Init();

    _usb_reenumerate();
    bsp_led_set(LED_BLINK, BSP_LED_ON);

    MX_USB_DEVICE_Init();
    bsp_led_set(LED_LINK, BSP_LED_ON);

    qtask_sched_init(&sched);
    // bsp_task_add("usb_send", _task_usb_send, 1);

    cli_init();
    cli_add("info", _cmd_info, "info command");
    bsp_led_set(LED_ERROR, BSP_LED_OFF);
    return 0;
}

int bsp_deinit()
{
    HAL_DeInit();
    return 0;
}

size_t ktime(void)
{
    return HAL_GetTick(); 
}

void bsp_usb_tx_ready()
{
    is_usb_tx_ready = true;
}

int bsp_send(const void *buf, size_t sz, size_t timeout)
{
    is_usb_tx_ready = false;
    uint8_t *p = (uint8_t *)buf;
    int retval = CDC_Transmit_HS(p, sz);

    uint32_t now = ktime();
    while(!is_usb_tx_ready) {
        if(ktime() - now > timeout) {
            return -HAL_TIMEOUT;
        }
    }
    if(retval == HAL_OK) {
        return sz;
    } else {
        return -retval;
    }
}

int usb_recved(uint8_t *data, size_t sz)
{
    return rb_write(&rb_usb_rx, data, sz);
}

int bsp_recv(void *buf, size_t sz, size_t timeout)
{
    size_t rsz = 0;
    size_t now = ktime();
    while(rsz < sz) {
        rsz += rb_read(&rb_usb_rx, (uint8_t *)buf + rsz, sz - rsz);
        if((ktime() - now) > timeout) {
            return -1;
        }
    }
    return rsz;
}

int kprintf(const char *fmt, ...)
{
    static char buf[256] = { 0 };
    va_list args;
    va_start(args, fmt);
    int sz = vsnprintf(buf, sizeof(buf) - 1, fmt, args);
    va_end(args);
    if(sz < 0) {
        return -1;
    }
    // return rb_write(&rb_usb_tx, (uint8_t *)buf, sz);
    return bsp_send(buf, sz, 10);
}

void bsp_led_set(BspLed led, BspLedState state)
{
    switch(led) {
    case LED_BLINK:
        if(state == BSP_LED_ON) {
            HAL_GPIO_WritePin(BLINK_GPIO_Port, BLINK_Pin, GPIO_PIN_RESET);
        } else {
            HAL_GPIO_WritePin(BLINK_GPIO_Port, BLINK_Pin, GPIO_PIN_SET);
        }
        break;
    case LED_LINK:
        if(state == BSP_LED_ON) {
            HAL_GPIO_WritePin(LINK_GPIO_Port, LINK_Pin, GPIO_PIN_RESET);
        } else {
            HAL_GPIO_WritePin(LINK_GPIO_Port, LINK_Pin, GPIO_PIN_SET);
        }
        break;
    case LED_ERROR:
        if(state == BSP_LED_ON) {
            HAL_GPIO_WritePin(ERROR_GPIO_Port, ERROR_Pin, GPIO_PIN_RESET);
        } else {
            HAL_GPIO_WritePin(ERROR_GPIO_Port, ERROR_Pin, GPIO_PIN_SET);
        }
        break;
    default:
        break;
    }
}

static size_t _blink_lfrq = 500;
static size_t _blink_hfrq = 300;

void blink_cb()
{
    static bool state = false;
    static size_t cnt = 0;
    static size_t frq = 0;
    if(cnt++ > frq) {
        state = !state;
        if(state) {
            bsp_led_set(LED_BLINK, BSP_LED_OFF);
            frq = _blink_hfrq;
        } else {
            bsp_led_set(LED_BLINK, BSP_LED_ON);
            frq = _blink_lfrq;
        }
        cnt = 0;
    }
}

void bsp_blink_set(size_t lfrq, size_t hfrq)
{
    if(lfrq > 0) {
        _blink_lfrq = lfrq;
    }
    if(hfrq > 0) {
        _blink_hfrq = hfrq;
    }
}

void bsp_delay_ms(size_t ms)
{
    HAL_Delay(ms);
}

void bsp_reboot(void)
{
    __disable_irq();
    HAL_NVIC_SystemReset();
}

int bsp_task_add(const char *name, QTaskHandle hdl, size_t period)
{
    QTaskObj *task_obj = qmem_alloc(&mempool, sizeof(QTaskObj));
    if(task_obj == NULL) {
        return -1;
    }
    return qtask_add(&sched, task_obj, name, hdl, period);
}

int bsp_task_del(const char *name)
{
    QTaskObj *task = qtask_obj(&sched, name);
    return qtask_del(&sched, task);
}

int bsp_task_suspend(const char *name)
{
    return qtask_suspend(&sched, name);
}

int bsp_task_resume(const char *name)
{
    return qtask_resume(&sched, name);
}

void bsp_task_exec()
{
    qtask_exec(&sched);
}

void systick_cb()
{
    qtask_tick_increase(&sched);
    blink_cb();
}

void *qmalloc(size_t size)
{
    return qmem_alloc(&mempool, size);
}

void qfree(void *ptr)
{
    qmem_free(&mempool, ptr);
}

